import java.util.concurrent.PriorityBlockingQueue;

public abstract class Employee extends Thread
{
	// Whether or not the employee is in the office.
	// SchedulableResource cannot do anything until they are in the office
	private volatile boolean isInOffice = false;
	private volatile boolean leftOffice = false;
	// The schedule for scheduling events during the day
	protected Schedule schedule = null;
	// The events that the employee needs to execute, organized by priority
	protected PriorityBlockingQueue<Event> eventsToExecute = new PriorityBlockingQueue<Event>();
	
	public Employee(Schedule schedule)
	{
		this.schedule = schedule;
	}

	/**
	 * Set the arrive to office flag, no events will execute until this flag is set.
	 */
	public void arriveToOffice()
	{
		isInOffice = true;
	}

	/**
	 * Set the leave office flag, no more events should occur after this is set.
	 */
	public void leaveOffice()
	{
		isInOffice = false;
		leftOffice = true;
	}
	
	public boolean leftOffice(){
		return leftOffice;
	}

	/**
	 * Returns a reference to the employees schedule
	 * 
	 * @return the employees schedule
	 */
	public Schedule getSchedule()
	{
		return schedule;
	}

	/**
	 * Should this employee print out events
	 * @return
	 */
	public boolean shouldPrintEvents()
	{
		return true;
	}

	public abstract void scheduleDay();

	@Override
	public void run()
	{
		scheduleDay();
		while (true)
		{
			try{
				dayLogistics();
			}catch(RuntimeException ex){
				break;
			}
		}
	}
	
	protected void dayLogistics(){
		Event event = null;
		try
		{
			event = eventsToExecute.take();
		}
		catch (InterruptedException e)
		{
			e.printStackTrace();
		}
		
		if (!isInOffice && !(event instanceof ArriveToOffice))
		{
			//Return the event to the queue
			eventsToExecute.offer(event);
			try
			{
				synchronized (this)
				{
					//Wait for the Arrive to Office Event
					wait();
				}
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
			return;
		}
		//Should we print the information about the event?
		if (shouldPrintEvents())
		{
			String message;
			//Avoid some timing issues and just use their exact time here
			//These events have a fixed time and should be treated as so
			if(event instanceof LeaveOffice || event instanceof ArriveToOffice){
				message = (event.getEventTime().toString() + " "
						+ toString() + " " + event.toString());
			}
			//These events have variable start times depending if people are busy
			else{
				message = (schedule.getCurrentTime().toString() + " "
						+ toString() + " " + event.toString());
			}
			PrintBuffer.printMessage(schedule.getCurrentTime(), message);
		}
		try{
			//Run any event logic now
			event.run();
		}catch(RuntimeException ex){
			eventsToExecute.offer(event);
		}
		//If this was the last event to happen, then exit out
		if (event instanceof LeaveOffice)
			throw new RuntimeException();
	}
	
	/**
	 * Add a new event to be executed by the employee.
	 * 
	 * NOTE: Events will be executed in a specific order given by their priority
	 * 
	 * @param event - Event to be executed
	 */
	synchronized public void execute(Event event)
	{
		eventsToExecute.offer(event);
		notifyAll();
	}

	@Override
	public String toString()
	{
		return getName();
	}
}
